package com.jiyongjun.component.core.model;

import com.jiyongjun.component.enums.LogConstant;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * <h2></h2>
 * <h3>描述：</h3>
 *
 * @author yongjun.jyj
 * @since 2022/7/14 19:21
 */
@Slf4j
public class LogContext {
    /** 为了同一个线程不同接口隔离，使用map */
    private static final ThreadLocal<Map<String, BaseLogContext<?>>> LOG_CONTEXT = ThreadLocal.withInitial(HashMap::new);
    private static final Map<Class<? extends BaseLogContext<?>>, Constructor<? extends BaseLogContext<?>>> CONSTRUCTOR_CACHE = new ConcurrentHashMap<>();

    public static BaseLogContext<?> getLogContext(Class<? extends BaseLogContext<?>> clazz, String apiName) {
        Map<String, BaseLogContext<?>> logVoMap = LOG_CONTEXT.get();

        return logVoMap.computeIfAbsent(apiName, name -> {
            try {
                Constructor<? extends BaseLogContext<?>> constructor = CONSTRUCTOR_CACHE.computeIfAbsent(clazz, c -> {
                    try {
                        Constructor<? extends BaseLogContext<?>> declaredConstructor = c.getDeclaredConstructor(String.class, ThreadLocal.class);
                        declaredConstructor.setAccessible(true);
                        return declaredConstructor;
                    } catch (NoSuchMethodException e) {
                        throw new RuntimeException(e);
                    }
                });
                return constructor.newInstance(apiName, LOG_CONTEXT);
            } catch (Exception e) {
                log.error("{}|创建日志上下文异常，exception:{}", LogConstant.LOG_EXCEPTION_KEYWORD, e.getMessage(), e);
                return null;
            }
        });
    }

    public static BaseLogContext<?> getLogContext(Class<? extends BaseLogContext<?>> clazz, String apiName, boolean createIfAbsent) {
        if (!createIfAbsent) {
            return LOG_CONTEXT.get().getOrDefault(apiName, null);
        }
        return getLogContext(clazz, apiName);
    }
}
